In [1]:
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
import matplotlib.pyplot as plt
import torch
from torch import nn
from torch import optim
import torch.nn.functional as F
from torchvision import datasets, transforms
import helper
import fc_model
In [2]:
# Define a transform to normalize the data
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,))])
# Download and load the training data
trainset = datasets.FashionMNIST('F_MNIST_data/',
download=True,
train=True,
transform=transform)
trainloader = torch.utils.data.DataLoader(dataset=trainset,
batch_size=64,
shuffle=True)
# Download and load the test data
testset = datasets.FashionMNIST('F_MNIST_data/',
download=True,
train=False,
transform=transform)
testloader = torch.utils.data.DataLoader(dataset=testset,
batch_size=64,
shuffle=True)
Here we can see one of the images.
In [3]:
image, label = next(iter(trainloader))
helper.imshow(image[0,:]);
To make things more concise here, I moved the model architecture and training code from the last part to a file called fc_model
. Importing this, we can easily create a fully-connected network with fc_model.Network
, and train the network using fc_model.train
. I'll use this model (once it's trained) to demonstrate how we can save and load models.
In [4]:
# Create the network, define the criterion and optimizer
model = fc_model.Network(784, 10, [512, 256, 128])
criterion = nn.NLLLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
In [5]:
fc_model.train(model,
trainloader,
testloader,
criterion,
optimizer,
epochs=2)
As you can imagine, it's impractical to train a network every time you need to use it. Instead, we can save trained networks then load them later to train more or use them for predictions.
The parameters for PyTorch networks are stored in a model's state_dict
. We can see the state dict contains the weight and bias matrices for each of our layers.
In [6]:
print("Our model: \n\n", model, '\n')
print("The state dict keys: \n\n", model.state_dict().keys())
The simplest thing to do is simply save the state dict with torch.save
. For example, we can save it to a file 'checkpoint.pth'
.
In [7]:
torch.save(model.state_dict(), './models/checkpoint.pth')
Then we can load the state dict with torch.load
.
In [8]:
state_dict = torch.load('./models/checkpoint.pth')
print(state_dict.keys())
And to load the state dict in to the network, you do model.load_state_dict(state_dict)
.
In [9]:
model.load_state_dict(state_dict)
Seems pretty straightforward, but as usual it's a bit more complicated. Loading the state dict works only if the model architecture is exactly the same as the checkpoint architecture. If I create a model with a different architecture, this fails.
This means we need to rebuild the model exactly as it was when trained. Information about the model architecture needs to be saved in the checkpoint, along with the state dict.
In [10]:
checkpoint = {'input_size': 784,
'output_size': 10,
'hidden_layers': [each.out_features for each in model.hidden_layers],
'state_dict': model.state_dict()}
torch.save(checkpoint, './models/checkpoint.pth')
Now the checkpoint has all the necessary information to rebuild the trained model. You can easily make that a function if you want. Similarly, we can write a function to load checkpoints.
In [11]:
def load_checkpoint(filepath):
checkpoint = torch.load(filepath)
model = fc_model.Network(checkpoint['input_size'],
checkpoint['output_size'],
checkpoint['hidden_layers'])
model.load_state_dict(checkpoint['state_dict'])
return model
In [12]:
model = load_checkpoint('./models/checkpoint.pth')
print(model)
In [13]:
for name, param in model.named_parameters():
if param.requires_grad:
print(name)
print(':')
print(param.data)
In [14]:
name, params = next(model.named_parameters())
In [15]:
name
Out[15]:
In [16]:
params
Out[16]: